package com.example.easy.excel.demo.utils;

import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.List;

@Slf4j
@Data
public class ExcelUtils<T> {

    private T value;

    private ExcelUtils() {
    }

    /**
     * 定义响应，设置文件下载响应头
     *
     * @param response 响应
     * @param fileName 文件名
     */
    public static void createResponse(HttpServletResponse response, String fileName) {
        // 设置响应头，使浏览器提示下载文件
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        // 获取当前时间
        String dateTime = LocalDateTime.now().toString().replace("-", "").replace(":", "").replace("T", "");
        // 定义文件名
        String finalFileName = fileName + dateTime.substring(0, 14) + ".xlsx";
        try {
            // 对其他浏览器进行 URL 编码
            String encodedFileName = URLEncoder.encode(finalFileName, StandardCharsets.UTF_8.toString());
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
                    "attachment; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName);
        }
        catch (UnsupportedEncodingException e) {
            log.error("编码文件名异常: {}", e.getMessage());
            throw new RuntimeException("文件生成出错");
        }
    }

//    /**
//     * 失败响应处理
//     *
//     * @param response 响应对象
//     */
//    public static void failedResponse(HttpServletResponse response) {
//        // 如果写出失败，重置响应并返回 JSON 错误信息
//        response.reset();
//        response.setContentType("application/json");
//        response.setCharacterEncoding("utf-8");
//        try {
//            PrintWriter writer = response.getWriter();
//            // 返回 JSON 错误提示
//            writer.println(JSON.toJSONString(ResponseMsg.fail(BusiException.ofDefaultCode("导出excel报表失败"))));
//        }
//        catch (IOException ex) {
//            log.error("返回 JSON 错误信息失败: {}", ex.getMessage(), ex);
//        }
//    }

    /**
     * 写入数据到Excel
     *
     * @param response  响应对象
     * @param sheetName 表单名
     * @param data      数据列表
     * @param clazz     泛型类型的 Class 对象
     */
    public static <T> void writeData(HttpServletResponse response, String sheetName, List<T> data, Class<T> clazz) {
        try {
            checkData(response, data);
            // 使用 EasyExcel 写入数据
            excelDataWrite(response, sheetName, data, clazz);
        }
        catch (IOException e) {
            log.error("写入 Excel 文件时出错", e);
            throw new RuntimeException("Excel 写入失败");
        }
        catch (Exception e) {
            log.error("未知的错误：{}", e.getMessage(), e);
            throw new RuntimeException("Excel 写入失败");
        }
    }

    /**
     * 写入数据到Excel
     *
     * @param response         响应对象
     * @param sheetName        表单名
     * @param data             数据列表
     * @param clazz            泛型类型的 Class 对象
     * @param head             表单实时头部设定
     * @param columnWidthStyle 列宽策略-手动设置列宽
     */
    public static <T> void writeData(HttpServletResponse response, String sheetName, List<T> data, Class<T> clazz, List<List<String>> head, AbstractColumnWidthStyleStrategy columnWidthStyle) {
        try {
            checkData(response, data);
            // 使用 EasyExcel 写入数据
            excelDataWrite(response, sheetName, data, clazz, head, columnWidthStyle);
        }
        catch (IOException e) {
            log.error("写入 Excel 文件时出错", e);
            throw new RuntimeException( "Excel 写入失败");
        }
        catch (Exception e) {
            log.error("未知的错误：{}", e.getMessage(), e);
            throw new RuntimeException("Excel 写入失败");
        }
    }

    /**
     * 写入数据到Excel
     *
     * @param response  响应对象
     * @param sheetName 表单名
     * @param data      数据列表
     * @param clazz     泛型类型的 Class 对象
     */
    public static <T> void writeDataInfo(HttpServletResponse response, String sheetName, List<T> data, Class<T> clazz, Converter converter, WriteHandler writeHandler) {
        try {
            checkData(response, data);
            // 使用 EasyExcel 写入数据
            excelDataWrite(response,sheetName,data,clazz,converter,writeHandler);
        }
        catch (IOException e) {
            log.error("写入 Excel 文件时出错", e);
            throw new RuntimeException( "Excel 写入失败");
        }
        catch (Exception e) {
            log.error("未知的错误：{}", e.getMessage(), e);
            throw new RuntimeException( "Excel 写入失败");
        }
    }

    /**
     * 校验数据
     *
     * @param response 响应对象
     * @param data 待写入的数据
     */
    public static <T> void checkData(HttpServletResponse response, List<T> data) {
        if (response == null) {
            log.error("HTTP response is null");
            throw new RuntimeException( "响应对象为空");
        }
        if (data == null || data.isEmpty()) {
            log.error("数据列表为空");
            throw new RuntimeException("数据为空");
        }
    }

    /**
     * 写入数据
     *
     * @param response  响应对象
     * @param sheetName 表单名
     * @param data      数据列表
     * @param clazz     泛型类型的 Class 对象
     */
    public static <T> void excelDataWrite(HttpServletResponse response, String sheetName, List<T> data, Class<T> clazz) throws IOException {
        try {
            EasyExcelFactory.write(response.getOutputStream(), clazz)
                    .sheet(sheetName)
                    .doWrite(data);
        }
        finally {
            //关闭流信息
            response.getOutputStream().close();
        }
    }

    /**
     * 写入数据
     *
     * @param response         响应对象
     * @param sheetName        表单名
     * @param data             数据列表
     * @param clazz            泛型类型的 Class 对象
     * @param head             表单实时头部设定
     * @param columnWidthStyle 列宽策略-手动设置列宽
     */
    public static <T> void excelDataWrite(HttpServletResponse response, String sheetName, List<T> data, Class<T> clazz, List<List<String>> head, AbstractColumnWidthStyleStrategy columnWidthStyle) throws IOException {
        EasyExcelFactory.write(response.getOutputStream(), clazz)
                .sheet(sheetName)
                .registerWriteHandler(columnWidthStyle)
                .head(head)
                .doWrite(data);
    }

    /**
     * 写入数据
     *
     * @param response  响应对象
     * @param sheetName 表单名
     * @param data      数据列表
     * @param clazz     泛型类型的 Class 对象
     * @param converter 转换器
     * @param writeHandler 写入处理器
     */
    private static <T> void excelDataWrite(HttpServletResponse response, String sheetName, List<T> data, Class<T> clazz, Converter converter, WriteHandler writeHandler) throws IOException{
        EasyExcelFactory.write(response.getOutputStream(), clazz)
                .registerConverter(converter)
                .sheet(sheetName)
                /*自适应列宽*/
                .registerWriteHandler(writeHandler)
                .doWrite(data);
    }

}